package cn.com.generaldata.os.service.impl;

import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.annotation.PostConstruct;

import org.apache.commons.lang3.StringUtils;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.sql.Criteria;
import org.nutz.json.Json;
import org.nutz.lang.Lang;
import org.nutz.lang.Stopwatch;
import org.nutz.resource.Scans;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.com.generaldata.jsme_indicator_system.entity.IndicatorEvaluation;
import cn.com.generaldata.jsme_indicator_system.util.Constants;
import cn.com.generaldata.os.service.indicator_constant.ProvinceIndicatorConstant;
import cn.com.generaldata.os.service.indicator_evaluation.CalculateService;
import cn.com.generaldata.os.service.indicator_evaluation.CalculateServiceHelper;
import cn.com.generaldata.os.service.indicator_evaluation.IndicatorEvaluationCalculateService;
import cn.com.generaldata.os.util.BeanMapper;
import cn.com.generaldata.os.vo.IndicatorAccountVO;

import com.google.common.collect.Maps;

@Service
public class IndicatorEvaluationService {

	private static final Logger logger = LoggerFactory.getLogger(IndicatorEvaluationService.class);

	@Autowired
	private Dao indicatorSystemDao;

	@Autowired
	private Dao dao;

	@Autowired
	private CalculateServiceHelper calculateServiceHelper;

	private final Map<String, CalculateService> calculateServiceCache = Maps.newHashMap();

	private final ExecutorService executorService = Executors.newSingleThreadExecutor();

	private boolean calculateTaskIsRunning;

	@PostConstruct
	public void init() {
		String packageName = IndicatorEvaluationCalculateService.class.getPackage().getName();
		List<Class<?>> clazzs = Scans.me().scanPackage(packageName);
		for (Class classOfT : clazzs) {
			if (CalculateService.class.isAssignableFrom(classOfT) && !Modifier.isAbstract(classOfT.getModifiers())) {
				String cacheName = classOfT.getSimpleName();
				try {
					CalculateService calculateService = (CalculateService) classOfT.newInstance();
					calculateService.setDao(dao);
					calculateService.setIndicatorSystemDao(indicatorSystemDao);
					calculateService.setCalculateServiceHelper(calculateServiceHelper);
					calculateServiceCache.put(cacheName, calculateService);
				} catch (InstantiationException e) {
					throw Lang.wrapThrow(e);
				} catch (IllegalAccessException e) {
					throw Lang.wrapThrow(e);
				}
			}
		}
	}

	/**
	 * WEB程序分章节计算评估指标数据.
	 * 
	 * @param indicatorAccountVO
	 */
	public void calculate(IndicatorAccountVO indicatorAccountVO) {
		if (!calculateTaskIsRunning) {
			executorService.execute(new CalIndicatorEvaluationTask(indicatorAccountVO));
		}
	}

	/**
	 * 分章节计算评估指标任务类
	 */
	class CalIndicatorEvaluationTask implements Runnable {

		private final IndicatorAccountVO indicatorAccountVO;

		public CalIndicatorEvaluationTask(IndicatorAccountVO indicatorAccountVO) {
			this.indicatorAccountVO = indicatorAccountVO;
		}

		@Override
		public void run() {
			calculateTaskIsRunning = true;

			try {
				logger.info("开始执行评估指标模型计算，计算信息: {}", Json.toJson(indicatorAccountVO));
				Stopwatch sw = Stopwatch.begin();
				IndicatorAccountVO vo = BeanMapper.map(indicatorAccountVO, IndicatorAccountVO.class);
				vo.setAreaId(ProvinceIndicatorConstant.NATION_AREA_ID);
				calculateIndicator(vo, Constants.INDICATOR_LEVEL_NATION, false);

				vo.setAreaId(ProvinceIndicatorConstant.PROVICE_AREA_ID);
				calculateIndicator(vo, Constants.INDICATOR_LEVEL_PROVINCE, false);

				for (String areaId : ProvinceIndicatorConstant.CITYS) {
					vo.setAreaId(areaId);
					calculateIndicator(vo, Constants.INDICATOR_LEVEL_CITY, false);
				}

				vo.setAreaId(ProvinceIndicatorConstant.NATION_AREA_ID);
				calculateIndicator(vo, Constants.INDICATOR_LEVEL_NATION, true);

				vo.setAreaId(ProvinceIndicatorConstant.PROVICE_AREA_ID);
				calculateIndicator(vo, Constants.INDICATOR_LEVEL_PROVINCE, true);

				for (String areaId : ProvinceIndicatorConstant.CITYS) {
					vo.setAreaId(areaId);
					calculateIndicator(vo, Constants.INDICATOR_LEVEL_CITY, true);
				}
				sw.stop();
				logger.info("结束执行评估指标模型计算，计算时间: {} 毫秒", sw.getDuration());
			} catch (Exception e) {
				logger.error("计算评估指标发生错误", e);
			}
			calculateTaskIsRunning = false;
		}

		private void calculateIndicator(IndicatorAccountVO vo, Integer level, boolean referenceLowerResult) {
			for (int priority = 10; priority > 0; priority--) {
				Criteria cri = Cnd.cri();
				cri.where().and("category1", "=", indicatorAccountVO.getCategory1());
				cri.where().and("category2", "=", indicatorAccountVO.getCategory2());
				cri.where().and("priority", "=", priority);
				cri.where().and("section", "=", level);
				if (referenceLowerResult) {
					cri.where().and("referenceLowerResult", "=", 1);
				} else {
					cri.where().and("referenceLowerResult", "=", 0);
				}
				calculate(vo, cri);
			}
		}

	}

	public void calculate(IndicatorAccountVO indicatorAccountVO, int level) {
		Criteria cri = Cnd.cri();
		cri.where().and("section", "=", level);
		cri.getOrderBy().desc("priority");
		calculate(indicatorAccountVO, cri);
	}

	public void calculate(IndicatorAccountVO indicatorAccountVO, Criteria cri) {
		List<IndicatorEvaluation> indicatorEvaluations = indicatorSystemDao.query(IndicatorEvaluation.class, cri);
		for (IndicatorEvaluation indicator : indicatorEvaluations) {
			if (StringUtils.isBlank(indicator.getCalculateClass())) {
				continue;
			}
			try {
				CalculateService calculateService = getCalculateService(indicator.getCalculateClass());
				calculateService.calculateIndicator(indicator, indicatorAccountVO);
			} catch (Exception e) {
				// logger.error("计算评估指标时发生错误,评估指标信息 : {},计算信息: {}", Json.toJson(indicator),
				// Json.toJson(indicatorAccountVO));
				// logger.error("", e);
				logger.error("计算评估指标时发生错误,评估指标信息id : {}-{},计算年份: {} ,错误原因: {} \r\n", indicator.getId(),
						indicator.getName(),
						indicatorAccountVO.getYear(), e.getMessage());
				// logger.error(e.getMessage(), e);
			}
		}
	}

	private CalculateService getCalculateService(String calculateClass) {
		return calculateServiceCache.get(calculateClass);
	}
}
